package mk.jug.academy.controller;

import java.util.Map;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;

import mk.jug.academy.factory.EventFactory;
import mk.jug.academy.model.Course;
import mk.jug.academy.model.Lecturer;
import mk.jug.academy.model.Page;
import mk.jug.academy.model.Student;
import mk.jug.academy.service.CourseService;
import mk.jug.academy.service.EventService;
import mk.jug.academy.service.LecturerService;
import mk.jug.academy.service.StudentService;
import mk.jug.academy.util.Utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;

/** 
 * View layer coordinator for the {@link Course} related functionality in the backend.
 *
 * @author Ice.Penov
 */
@Controller
public class CourseAdminController extends BaseController {
	
	private static final Logger LOG = Logger.getLogger(CourseAdminController.class.getName());
	
	//TODO: Externalize this.
	public static final int itemsPerPage = 8;
	
	@Autowired private CourseService courseService;
	@Autowired private LecturerService lecturerService;
	@Autowired private StudentService studentService;
	@Autowired private EventService eventService;
	
	private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
	
	private static String CREATE_COURSE = "create.course";
	private static String LIST_HATCHING = "admin.list.hatching";
	private static String LIST_AVAILABLE = "admin.list.available";
	private static String LIST_INPROGRESS = "admin.courses.inprogress";
	private static String EDIT_COURSE = "admin.edit.course";
	private static String VIEW_COURSE = "admin.courses.view";
	private static String COURSE_PARTICIPANTS = "course.participants";
	
	@RequestMapping("/admin/courses/hatching.do")
	public String listHatching(ModelMap map, @RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "highlightID", required = false) String highlightID) {
		if (page == null) {
			page = 1;
		}
		Page<Course> pagedCourses = courseService.listHatching(page, itemsPerPage);
		map.addAttribute("courses", pagedCourses.getItems());
		map.addAttribute("pages", Utils.getPageNumber(itemsPerPage, pagedCourses.getTotalCount()));
		map.addAttribute("page", page);
		map.addAttribute("highlightID", highlightID);
		return LIST_HATCHING;
	}
	
	@RequestMapping("/admin/courses/view.do")
	public String view(final ModelMap map, final @RequestParam("id") Long id) {
		return new ControllerContext(map) {
			public String execute() {
				Course course = courseService.load(id);
				map.addAttribute("course", course);
				return VIEW_COURSE;
			}
		}.result();
	}
	
	@RequestMapping("/admin/courses/inprogress.do")
	public String listInProgress(final ModelMap map, final @RequestParam(value = "page", required = false) Integer p, final @RequestParam(value = "highlightID", required = false) String highlightID) {
		return new ControllerContext(map) {
			public String execute() {
				Integer page = p;
				if (page == null) {
					page = 1;
				}
				Page<Course> pagedCourses = courseService.listInProgress(page, itemsPerPage);
				map.addAttribute("courses", pagedCourses.getItems());
				map.addAttribute("pages", Utils.getPageNumber(itemsPerPage, pagedCourses.getTotalCount()));
				map.addAttribute("page", page);
				map.addAttribute("highlightID", highlightID);
				return LIST_INPROGRESS;
			}
		}.result();
	}
		
	@RequestMapping("/admin/courses/hatch.do")
	public String hatch(final ModelMap map, final @RequestParam("id") Long id) {
		return new ControllerContext(map) {
			public String execute() {
				String email = SecurityContextHolder.getContext().getAuthentication().getName();
				Lecturer lecturer = lecturerService.findByEmail(email);				
				Course course = courseService.load(id);
				courseService.hatchCourse(course);
				eventService.createEvent(EventFactory.createCourseHatchingEvent(course, lecturer));
				return "redirect:/admin/courses/available.do?highlightID=" + course.getId();				
			}
		}.result();
	}
	
	@RequestMapping("/admin/courses/assignStudentToCourse.do")
	public String assignStudentToCourse(final ModelMap map, final @RequestParam("courseID") Long courseID, final @RequestParam("studentID") Long studentID, final @RequestParam("redirectURL") String redirectURL) {
		return new ControllerContext(map) {
			public String execute() {
				Course course = courseService.load(courseID);
				Student student = studentService.load(studentID);
				courseService.assignStudent(course, student);
				return "redirect:" + redirectURL;
			}
		}.result();
	}
	
	@RequestMapping("/admin/courses/removeStudentFromCourse.do")
	public String removeStudentFromCourse(final ModelMap map, final @RequestParam("courseID") Long courseID, final @RequestParam("studentID") Long studentID, final @RequestParam("redirectURL") String redirectURL) {
		return new ControllerContext(map) {
			public String execute() {
				Course course = courseService.load(courseID);
				Student student = studentService.load(studentID);
				courseService.removeStudent(course, student);
				return "redirect:" + redirectURL;
			}
		}.result();
	}
	
	@RequestMapping("/admin/courses/courseParticipants.do")
	public String courseParticipants(final ModelMap map, final @RequestParam("courseID") Long id) {
		return new ControllerContext(map) {
			public String execute() {
				Course course = courseService.load(id);
				map.addAttribute("course", course);
				map.addAttribute("assignedStudents", courseService.fetchStudentsForCourse(course));
				map.addAttribute("availableStudents", studentService.fetchWaiting());
				map.addAttribute("availableLecturers", lecturerService.fetchActive());
				return COURSE_PARTICIPANTS;
			}
		}.result();
	}
	
	@RequestMapping("/admin/courses/assignLecturer.do")
	public String assignLecturer(final ModelMap map, final @RequestParam("lecturerID") Long lecturerID, final @RequestParam("courseID") Long courseID, final @RequestParam("redirectURL") String redirectURL) {
		return new ControllerContext(map) {
			public String execute() {
				Lecturer lecturer = lecturerService.load(lecturerID);
				Course course = courseService.load(courseID);
				courseService.assignLecturer(course, lecturer);
				return "redirect:" + redirectURL;
			}
		}.result();
	}
	
	@RequestMapping("/admin/courses/startCourse.do")
	public String startCourse(final ModelMap map, final @RequestParam("id") Long id, final @RequestParam("redirectURL") String redirectURL) {
		return new ControllerContext(map) {
			public String execute() {
				Course course = courseService.load(id);
				courseService.startCourse(course);
				eventService.createEvent(EventFactory.createCourseStartEvent(course));				
				return "redirect:" + redirectURL;
			}
		}.result();
	}		
	
	@RequestMapping("/admin/courses/edit.do")
	public String editCourse(final ModelMap map, final @RequestParam("id") Long id) {
		return new ControllerContext(map) {
			public String execute() {
				Course course = courseService.load(id);
				map.addAttribute("course", course);
				map.addAttribute("formURL", blobstoreService.createUploadUrl("/admin/courses/saveCourse.do"));				
				return EDIT_COURSE;
			}
		}.result();
	}
	
	@RequestMapping("/admin/courses/finishCourse.do")
	public String finishCourse(final ModelMap map, final @RequestParam("id") Long id, final @RequestParam("redirectURL") String redirectURL) {
		return new ControllerContext(map) {
			public String execute() {
				Course course = courseService.load(id);
				courseService.finishCourse(course);
				LOG.info("Course [" + course + "] has finished.");
				return "redirect:" + redirectURL;
			}
		}.result();
	}
	
	@RequestMapping("/admin/courses/available.do")
	public String listAvailable(ModelMap map, @RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "highlightID", required = false) String highlightID) {
		if (page == null) {
			page = 1;
		}
		Page<Course> pagedCourses = courseService.listAvailable(page, itemsPerPage);
		map.addAttribute("courses", pagedCourses.getItems());
		map.addAttribute("pages", Utils.getPageNumber(itemsPerPage, pagedCourses.getTotalCount()));
		map.addAttribute("page", page);
		map.addAttribute("highlightID", highlightID);
		return LIST_AVAILABLE;		
	}
	
	@RequestMapping(value = "/admin/courses/createNewCourse.do")
	public String createNewCourse(ModelMap map) {
		map.addAttribute("course", new Course());
		map.addAttribute("formURL", blobstoreService.createUploadUrl("/admin/courses/processCourseCreation.do"));
		return CREATE_COURSE;
	}
	
	@RequestMapping(value = "/admin/courses/processCourseCreation.do")
	public String processCourseCreation(HttpServletRequest request, @ModelAttribute("course") Course course, ModelMap map) {
		Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(request);
		BlobKey key = blobs.get("program");
		if (key != null) {
			course.setFileKey(key.getKeyString());
		}
		String email = SecurityContextHolder.getContext().getAuthentication().getName();
		Lecturer lecturer = lecturerService.findByEmail(email);
		courseService.submitNewCourse(course);
		eventService.createEvent(EventFactory.createCourseCreationEvent(course, lecturer));
		return "redirect:/admin/courses/hatching.do?highlightID=" + course.getId();
	}
	
	@RequestMapping(value = "/admin/courses/saveCourse.do")
	public String saveCourse(final ModelMap map, final HttpServletRequest request, final @ModelAttribute("course") Course course, final @RequestParam("id") Long id) {
		return new ControllerContext(map) {
			public String execute() {
				Course loaded = courseService.load(id);
				loaded.setTitle(course.getTitle());
				loaded.setDescription(course.getDescription());
				Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(request);
				BlobKey key = blobs.get("program");
				if (key != null) {
					loaded.setFileKey(key.getKeyString());
				}
				courseService.saveChanges(loaded);
				return "redirect:/admin/courses/hatching.do?highlightID=" + course.getId();
			}
		}.result();
	}
	
	
}